﻿using Infrastructure.Extension;
using Infrastructure.Utils;
using Infrastructure.WebApi.Middleware;
using Microsoft.AspNetCore.Http;
using Neoit.Utils.Extensions;
using Serilog.Events;
using System.Diagnostics;
using System.Net;

namespace Infrastructure.WebApi;

/// <summary>
/// 统一错误异常处理中间件类
/// </summary>
public class ErrorHandlingMiddleware
{
    private const string RESPONSE_HEADER_RESPONSE_TIME = "X-Response-Time-ms";
    private readonly RequestDelegate next;
    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }
    public async Task Invoke(HttpContext context)
    {
        var guid = Convert.ToString(context.Request.Headers["X-Request-Id"]) ?? Guid.NewGuid().ToString("N");
        context.Request.Headers["X-Request-Id"] = guid;
        var log = new HttpLogInfo
        {
            Url = context.Request.GetAbsoluteUri(),
            HttpMethod = context.Request.Method,
            UserIp = context.GetUserIp(),
            Rid = guid
        };
        _Timer(context, log);//耗时记录
        #region Request-Body
        if (context.Request.Method.ToLower() != "get" && (context.Request?.ContentType?.Contains("application/json")).ToBool())
        {
            log.RequestBody = await context.Request.ReadBodyAsStringAsync();
        }
        #endregion
        try
        {
            await next(context);
            log.Tag = LogTag.RequestLog.ToString();
            log.LogEventLevel = LogEventLevel.Information;
        }
        catch (Exception ex)
        {
            log.Tag = LogTag.Exception.ToString();
            log.Exception = ex;
            await _handleExceptionAsync(context, ex, log);
        }
        finally
        {
            #region Response-Body
            if (context.Request.Method.ToLower() != "get" && (context.Request?.ContentType?.Contains("application/json")).ToBool())
            {
                //log.ResponseBody = await context.Response.Body.
            }
            #endregion
            log.StatusCode = context.Response.StatusCode;
            //Log.Write(log.LogEventLevel, log.Exception,
            //   "{HttpMethod} {Url} {StatusCode} {RequestBody} {Tag} {UserIp} {Rid} in {Time} ms",
            //    log.HttpMethod, log.Url, log.StatusCode, log.RequestBody, log.Tag, log.UserIp, log.Rid, log.Time);
        }
    }
    private static Task _handleExceptionAsync(HttpContext context, Exception ex, HttpLogInfo log)
    {
        var code = ApiCode.Error;
        string msg;
        if (ex is BusinessException bex)
        {
            context.Response.StatusCode = (int)bex.HttpStatusCode;
            code = bex.Code;
            msg = bex.Message;
            log.LogEventLevel = LogEventLevel.Warning;
        }
        else
        {
            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
            log.LogEventLevel = LogEventLevel.Error;
            if (EnvironmentVariableHelper.IsAspNetCoreProduction)
            {
                msg = "系统繁忙，请稍后再试";
            }
            else
            {
                msg = ex.Combine();
            }
        }
        var data = new ResponseBase(msg, code) { Rid = log.Rid };
        var result = data.ToJson();
        context.Response.ContentType = "application/json;charset=utf-8";
        return context.Response.WriteAsync(result);
    }

    private void _Timer(HttpContext context, HttpLogInfo log)
    {
        var watch = new Stopwatch();
        watch.Start();
        context.Response.OnStarting(() =>
        {
            watch.Stop();
            var responseTime = watch.ElapsedMilliseconds;
            context.Response.Headers[RESPONSE_HEADER_RESPONSE_TIME] = responseTime.ToString();
            if (responseTime / 1000 > 2) log.LogEventLevel = log.LogEventLevel < LogEventLevel.Warning ? LogEventLevel.Warning : log.LogEventLevel;
            log.Time = responseTime;
            return Task.CompletedTask;
        });
    }
}